package idao.engine;

import idao.ConnectionManager;

import java.sql.Connection;
import java.sql.SQLException;

class TxManager
{

    private static class Tx
    {
        int nested = 0; // number of nested Tx
        int failed = 0; // number of failed Tx
        Connection conn; // SQL connection of this Tx

        Tx()
        {/* deal with the synthetic accessor method */
        }
    }

    private static ThreadLocal<Tx> tx = new ThreadLocal<Tx>();

    static void startTx()
    {
        Tx t = tx.get();
        if (t == null) {
            // no Tx, start a new one
            t = new Tx();
            tx.set(t);
        } else {
            // join the existing Tx
            t.nested++;
        }
        // always assumes it's gonna fail
        t.failed++;
    }

    static Connection getTxConnection(ConnectionManager cm)
    {
        Tx t = tx.get();
        // no Tx, maybe in a container, get a new connection
        if (t == null) {
            return cm.getConnection();
        }
        // no connection, get an new one
        if (t.conn == null) {
            t.conn = cm.getConnection();
        }
        return t.conn;
    }

    static void commitTx()
    {
        Tx t = tx.get();
        if (t == null) {
            throw new IllegalStateException("Transaction not started yet!");
        }
        // Do not commit Tx here, just marks it as success
        t.failed--;
    }

    static void endTx() throws SQLException
    {
        Tx t = tx.get();
        // Do not end if this is not the out most Tx,
        // that is, the Tx who initially started this transaction.
        if ((t != null) && (t.nested-- == 0)) {
            try {
                if (t.conn != null) {
                    commitOrRollback(t);
                }
            } finally {
                tx.set(null);
            }
        }
    }

    private static void commitOrRollback(Tx t) throws SQLException
    {
        try {
            if (t.failed == 0) {
                // all Tx succeeded, commit
                t.conn.commit();
            } else {
                // some Tx failed, rollback
                t.conn.rollback();
            }
        } finally {
            DaoUtils.close(t.conn);
        }
    }

    private TxManager()
    {
        // no instantiation needed.
    }
}
